package com.xianyu.open.restclient.proxy;

import com.alibaba.fastjson.JSON;
import com.xianyu.open.restclient.config.MethodInfo;
import com.xianyu.open.restclient.config.RestClientProperties;
import com.xianyu.open.restclient.config.ServerInfo;
import com.xianyu.open.restclient.constants.TypeAndMethodMap;
import com.xianyu.open.restclient.handler.RestClientHandler;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.lang.reflect.Proxy;
import java.util.Map;

/**
 *
 * @author bailinan
 * @date 2018/6/16
 */
public class JdkProxyCreator implements ProxyCreator {

    @Override
    public Object createProxy(Class<?> type) {
        RestClientHandler handler = RestClientHandler.getWebMvcInstance();
        return Proxy.newProxyInstance(type.getClassLoader(), new Class[]{type}, (proxy, method, args) -> {
            MethodInfo methodInfo = TypeAndMethodMap.typeAndMethodInfo
                    .get(type.getCanonicalName()).get(method.toString());
            if (methodInfo == null) {
                String proxyName = proxy.getClass().getName();
                return method.invoke(type.getName() + proxyName.substring(proxyName.lastIndexOf(".")), args);
            }
            Class<?> returnType = method.getReturnType();
            methodInfo.setResponseType(returnType);
            if (returnType.getCanonicalName().equals(ResponseEntity.class.getCanonicalName())){
                String toGenericString = method.toGenericString();
                toGenericString = toGenericString.substring(toGenericString.indexOf("ResponseEntity")+15);
                toGenericString = toGenericString.substring(0,toGenericString.indexOf(">"));
                if (toGenericString.contains("<")){
                    toGenericString = toGenericString.substring(0,toGenericString.indexOf("<"));
                }
                methodInfo.setResponseEntityType(Class.forName(toGenericString));
            }
            setHttpParams(methodInfo, method, args);
            return handler.invokeHandler(TypeAndMethodMap.typeAndServerInfo
                    .get(type.getCanonicalName()),methodInfo,returnType);
        });
    }

    @SuppressWarnings("unchecked")
    private void setHttpParams(MethodInfo methodInfo, Method method, Object[] args) {
        Parameter[] parameters = method.getParameters();
        for (int i=0; i<parameters.length; i++){
            PathVariable pathVariable = parameters[i].getAnnotation(PathVariable.class);
            if (pathVariable != null){
                methodInfo.getUriVariables().put(pathVariable.value(), args[i]);
                continue;
            }
            RequestParam requestParam = parameters[i].getAnnotation(RequestParam.class);
            if (requestParam!=null){
                if (methodInfo.getRequestBody() != null){
                    throw new RuntimeException("requestParam与requestBody不可同时使用");
                }
                if (args[i] instanceof Map){
                    Map paramMap = (Map) args[i];
                    paramMap.forEach((key, value) -> {
                        methodInfo.getRequestParam().put(key.toString(), value);
                    });
                }else {
                    methodInfo.getRequestParam().put(requestParam.value(), args[i]);
                }
                continue;
            }

            ModelAttribute modelAttribute = parameters[i].getAnnotation(ModelAttribute.class);
            if (modelAttribute != null){
                Map<String, Object> tempMap = null;
                if (args[i] instanceof Map){
                    tempMap = (Map) args[i];
                }else {
                    tempMap = JSON.parseObject(JSON.toJSONString(args[i]), Map.class);
                }
                methodInfo.getRequestParam().putAll(tempMap);
            }

            RequestHeader requestHeader = parameters[i].getAnnotation(RequestHeader.class);
            if (requestHeader != null && args[i]!=null){
                methodInfo.getRequestHeader().put(requestHeader.value(), args[i].toString());
                continue;
            }

            RequestBody requestBody = parameters[i].getAnnotation(RequestBody.class);
            if (requestBody != null) {
                if (methodInfo.getRequestParam().size() > 0){
                    throw new RuntimeException("requestParam与requestBody不可同时使用");
                }
                methodInfo.getRequestHeader().putIfAbsent("Content-Type","application/json");
                methodInfo.setRequestBody(args[i]);
            }
        }

        if (methodInfo.getRequestParam().size() > 0 && methodInfo.getHttpMethod() == HttpMethod.GET){
            // 构造get请求的参数及连接
            buildGetParam(methodInfo);
        }
    }

    /**
     * 完善get请求参数
     * @param methodInfo
     * @return
     */
    private void buildGetParam(MethodInfo methodInfo){
        Map<String, Object> requestParam = methodInfo.getRequestParam();
        String url = methodInfo.getUrl();
        StringBuffer buffer = new StringBuffer(url).append("?");
        requestParam.forEach((name, value) -> buffer.append(name)
                .append("=")
                .append(value)
                .append("&"));
        //去掉最后一个&

        methodInfo.setRelUrl(buffer.substring(0, buffer.length() - 1));
    }

}
